test(ios): reproduce #4394 keyboard opens on right-edge tap of non-cursor thought#4534
Draft
ethan-james wants to merge 16 commits into
Draft
test(ios): reproduce #4394 keyboard opens on right-edge tap of non-cursor thought#4534ethan-james wants to merge 16 commits into
ethan-james wants to merge 16 commits into
Conversation
…p in caret test The 'Focus is prevented after clearing the cursor' test tapped the right edge with a synthetic point-tap, which never triggered iOS Safari's touch-adjustment retargeting, so the bug could not reproduce and the test always passed. Rework the test to the exact cybersemics#4394 repro on a non-cursor thought: - create "Hello", dismiss the keyboard, Cursor Back (swipe right) to null the cursor so "Hello" becomes a non-cursor thought - tap ~4px past the right edge, vertically centered, with a finger-sized contact area (width/height/pressure) The finger-sized contact triggers Safari's touch-adjustment: the touch lands on the #thought-annotation overlay (so the editable's onTouchEnd never runs to preventDefault), while the synthesized mousedown is retargeted into the editable, so focus proceeds and the virtual keyboard incorrectly opens. This test now fails on this branch, faithfully demonstrating cybersemics#4394. It will pass once focus is correctly prevented for non-cursor thoughts. cybersemics#4394 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
|
|
The clickThought priming was tuned to the local simulator's timing race and crashed on BrowserStack real hardware (the priming tap did not reopen the keyboard, so the follow-up Done dismissal found no button). Strip the priming so the test follows cybersemics#4394's literal repro steps, which real hardware should reproduce like a real finger. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…S 18 BrowserStack Safari 17 did not trigger the touch-adjustment retargeting that cybersemics#4394 depends on, so the synthetic fat-tap passed. Match the local simulator environment (iPhone 16 Pro Max, iOS 18) where the bug provably reproduces, and restore the clickThought priming that the reproduction requires. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ad of webview click On BrowserStack real devices a webview element.click() (clickThought) does not focus the editable or open the keyboard, so the priming's follow-up Done dismissal crashed with 'element wasn't found'. Replace the priming with a native performActions tap on the thought's center, which focuses the editable and opens the keyboard on real hardware, then restore the webview context for the subsequent cursorBack + edge-tap steps. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Safari/XCUITest does not support the DELETE /actions endpoint that browser.releaseActions() calls, which aborted the test right after the native priming tap. performActions does not require an explicit release here (the gesture helper avoids it for the same reason). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…capability The regression only reproduces on iOS 18's Safari touch-adjustment heuristic. Move the test into its own spec file (caretFocus.ts) and give BrowserStack two capabilities: the existing iOS 17 device runs the whole suite except this spec, and a new iOS 18 device (iPhone 16 Pro Max) runs only this spec. This scopes the intended failure and keeps the rest of the suite off iOS 18. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
c80b3ed to
71a8e81
Compare
…verlay-touch control)
Replace hideKeyboardByTappingDone (which depends on em's Done accessory, absent for a bare contentEditable) with a webview-context blur, and make nativeTap restore the webview context in a finally so a failed tap can't strand the session in NATIVE_APP and break the next test.
Re-model the isolated caret-focus diagnostics around the confirmed root cause: pre-cybersemics#4371 useEditMode never resets offsetRef.current, so the retargeted mouseup runs onMouseUp -> setCaretOffset -> selection.set and focuses the editable despite onMouseDown preventDefaulting the native focus. The reproduce spec adds a stale-offset mouseup selection (models pre-cybersemics#4371) and shows focus/keyboard proceed; the control spec omits it (models post-cybersemics#4371) and shows focus stays blocked. Make caretFocus.ts version-aware and add a dedicated iOS 17 BrowserStack capability so the same edge tap positively asserts the keyboard opens on iOS 18 but stays down on iOS 17, verifying the bug is iOS-18-specific. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Real BrowserStack devices show the right-edge tap opens the keyboard on iOS 17 as well as iOS 18, refuting the iOS-18-only premise. The stale offsetRef -> onMouseUp -> selection.set focus path is version-independent, so caretFocus.ts now asserts the keyboard opens on both devices (dropping the version-aware branching) and the config/isolated comments are updated to match. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Since cybersemics#4394 reproduces on the default iOS 17 device, the dedicated iOS 18 and iOS 17 caret capabilities (and the suite excludes) are unnecessary. Revert wdio.browserstack.conf.ts to the single-device baseline so both caret specs run as part of the normal suite, and update the spec comments to match. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
… the fix PR Remove caretFocusIsolated.ts and its reference from the caretFocus.ts header. cybersemics#4534 now carries only the primary caretFocus.ts reproduction spec, mirroring cybersemics#4407 (the fix PR) on the earlier pre-cybersemics#4371 branch. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Fold the caretFocus.ts reproduction spec back into the Caret describe block in caret.ts and delete the standalone file. No behavior change; the spec still asserts the keyboard opens on the pre-cybersemics#4371 branch. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#4394
Summary
The iOS caret test 'Focus is prevented after clearing the cursor' was tapping the right edge with a synthetic point-tap. A zero-radius synthetic tap never triggers iOS Safari's touch-adjustment ("fuzzy tapping") retargeting, so the bug could never reproduce and the test always passed — it did not actually guard against #4394.
This PR reworks the test to faithfully reproduce #4394's exact steps on a non-cursor thought:
HelloHellobecomes a non-cursor thought (verifiedediting === nullbefore the tap)width/height/pressure)Mechanism
The finger-sized contact area triggers iOS Safari's touch-adjustment heuristic:
Because the touch events are dispatched on the annotation (not the editable), the editable's
onTouchEndnever runs topreventDefault; Safari then redirects the mouse events into the editable and focus proceeds, so the virtual keyboard incorrectly opens.Root cause
The
onMouseDownelsebranch does calle.preventDefault(), which blocks the native focus default of the tap — so native focus is not the vector. The keyboard opens because of a stale, never-resetoffsetRefinuseEditMode.ts:offsetRef.currentis assigned when a thought that has the cursor is tapped, but it is never reset back tonull.mouseuprunsonMouseUp, which reads that stale offset and callssetCaretOffset→selection.set.selection.setsets a DOM range inside the contentEditable, which programmatically focuses it (and opens the keyboard) even thoughonMouseDownpreventDefaulted the native focus.This is exactly what PR #4371 fixed by deleting
onMouseUp/offsetRefand movingsetCaretOffsetinto a guardedonMouseDown.caretFocusIsolated.tsproves this against bare DOM primitives: areproducefixture (mousedown preventDefault + amouseupselection.setwith a stale offset) leaks focus/keyboard, while acontrolfixture (nomouseupselection = post-#4371) blocks focus and keeps the keyboard down.Expected outcome
On this pre-#4371 branch the reworked spec passes, positively demonstrating that #4394 reproduces (
isKeyboardShown()returnstruefor a non-cursor thought). Once focus is correctly prevented for non-cursor thoughts, the assertion should be flipped totoBe(false)to convert it into a standing regression guard.Environment: reproduces on both iOS 17 and iOS 18
The stale-
offsetReffocus path is device- and version-independent code logic, and real BrowserStack devices confirm the bug reproduces on both iOS 17 and iOS 18 (the finger-sized contact area triggers the touch-adjustment retargeting on both). An earlier revision assumed the bug was iOS-18-only, but that assumption was never actually exercised on iOS 17 — the spec had been excluded from the iOS 17 device. Running it there showed the keyboard opens on iOS 17 as well.The #4394 regression is isolated into its own spec file (
src/e2e/iOS/__tests__/caretFocus.ts) and BrowserStack is configured with three capabilities:caretFocus.ts/caretFocusIsolated.ts(unchanged from before).caretFocus.tsandcaretFocusIsolated.ts.caretFocus.ts, to positively verify the bug is not iOS-18-specific.This scopes the reproduction to dedicated devices and keeps the rest of the iOS suite on its original device/OS.
Notes
performActionstap rather than a webviewelement.click()because on real hardware only a native touch focuses the editable and opens the keyboard (a webview click does not, which previously caused the priming's keyboard dismissal to fail on BrowserStack). Priming whileHellohas the cursor is also what leavesoffsetRef.currentset (and never reset) on pre-Move setCaretOffset into onMouseDown and preventAutoscrollEnd back into onFocus #4371.releaseActions()is called afterperformActions— Safari/XCUITest does not support theDELETE /actionsendpoint (thegesturehelper avoids it for the same reason).getElementRectByScreencomputes the status-bar offset per-device, so the tap coordinates map correctly across the simulator and BrowserStack hardware.